﻿(function() {
var El = Ext.Element = Ext.extend(Object, {
    
    defaultUnit : "px",

    //创建一个新的 Element.
    //element：String/HTMLElement
    //forceNew：Boolean 默认情况下，如果缓存中有这个元素，则返回相同元素，选择true则跳过检查
    constructor : function(element, forceNew) {
        var dom = typeof element == 'string'
                ? document.getElementById(element)
                : element,
            id;

        if (!dom) {//文档中没有这个元素，直接返回
            return null;
        }

        id = dom.id;
        if (!forceNew && id && Ext.cache[id]) {//缓存中已经存在该元素，且forceNew不为true，则含糊缓存中的元素
            return Ext.cache[id].el;
        }

        
        this.dom = dom;


        this.id = id || Ext.id(dom); //对页面元素生成唯一id
        return this;
    },

    //将传入的参数作为元素的参数
    //o：参数对象
    //useSet：(optional) false to override the default setAttribute to use expandos.
    set : function(o, useSet) {
        var el = this.dom,
            attr,
            value;

        for (attr in o) {
            if (o.hasOwnProperty(attr)) {
                value = o[attr];
                if (attr == 'style') {
                    this.applyStyles(value);
                }
                else if (attr == 'cls') {
                    el.className = value;
                }
                else if (useSet !== false) {
                    el.setAttribute(attr, value);
                }
                else {
                    el[attr] = value;
                }
            }
        }
        return this;
    },

    //Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
    is : function(simpleSelector) {
        return Ext.DomQuery.is(this.dom, simpleSelector);
    },

    //Returns the value of the "value" attribute（this.dom.value）
    //asNumber：Boolean。 true to parse the value as a number
    getValue : function(asNumber){
        var val = this.dom.value;
        return asNumber ? parseInt(val, 10) : val;
    },

    
    addListener : function(eventName, fn, scope, options){
        Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);
        return this;
    },

    
    removeListener : function(eventName, fn, scope) {
        Ext.EventManager.un(this.dom, eventName, fn, scope);
        return this;
    },

    
    removeAllListeners : function(){
        Ext.EventManager.removeAll(this.dom);
        return this;
    },

    
    purgeAllListeners : function() {
        Ext.EventManager.purgeElement(this, true);
        return this;
    },

    //Removes this element's dom reference. Note that event and cache removal is handled at
    remove : function() {
        var me = this,
            dom = me.dom;

        if (dom) {
            delete me.dom;
            Ext.removeNode(dom);
        }
    },

    //
    isAncestor : function(c) {
        var p = this.dom;
        c = Ext.getDom(c);
        if (p && c) {
            return p.contains(c);
        }
        return false;
    },

    //Determines if this element is a descendent of the passed in Element.
    //p：An Ext.Element, HTMLElement or string linking to an id of an Element.
    isDescendent : function(p) {
        return Ext.fly(p, '_internal').isAncestor(this);
    },

    //Returns true if this element is an ancestor(祖先) of the passed element
    contains : function(el) {
        return !el ? false : this.isAncestor(el);
    },

    //Returns the value of an attribute from the element's underlying DOM node.
    //name：The attribute name
    //ns：(optional) The namespace in which to look for the attribute
    getAttribute : function(name, ns) {
        var d = this.dom;
        return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
    },

    //Set the innerHTML of this element
    setHTML : function(html) {
        if(this.dom) {
            this.dom.innerHTML = html;
        }
        return this;
    },

    //Returns the innerHTML of an Element or an empty string if the element's dom no longer exists.
    getHTML : function() {
        return this.dom ? this.dom.innerHTML : '';
    },

    //Hide this element - Uses display mode to determine whether to use "display" or "visibility".
    hide : function() {
        this.setVisible(false);
        return this;
    },

    //Show this element - Uses display mode to determine whether to use "display" or "visibility"
    show : function() {
        this.setVisible(true);
        return this;
    },

    //Sets the visibility of the element (see details). 
    //If the visibilityMode is set to Element.DISPLAY, 
    //it will use the display property to hide the element, 
    //otherwise it uses visibility. The default is to hide and show using the visibility property.
     setVisible : function(visible, animate) {
        var me = this,
            dom = me.dom,
            mode = this.getVisibilityMode();

        switch (mode) {
            case El.VISIBILITY: //Visibility(能见度) mode constant for use with setVisibilityMode. Use visibility to hide element
                this.removeCls(['x-hidden-display', 'x-hidden-offsets']);
                this[visible ? 'removeCls' : 'addCls']('x-hidden-visibility');
            break;

            case El.DISPLAY: //Visibility(能见度) mode constant for use with setVisibilityMode. Use display to hide element
                this.removeCls(['x-hidden-visibility', 'x-hidden-offsets']);
                this[visible ? 'removeCls' : 'addCls']('x-hidden-display');
            break;

            case El.OFFSETS: //Visibility(能见度) mode constant for use with setVisibilityMode. Use offsets(偏移) to hide element
                this.removeCls(['x-hidden-visibility', 'x-hidden-display']);
                this[visible ? 'removeCls' : 'addCls']('x-hidden-offsets');
            break;
        }

        return me;
    },
    //
    getVisibilityMode: function() {
        var dom = this.dom,
            mode = El.data(dom, 'visibilityMode');

        if (mode === undefined) {
            El.data(dom, 'visibilityMode', mode = El.DISPLAY);
        }

        return mode;
    },
    //
    setVisibilityMode : function(mode) {
        El.data(this.dom, 'visibilityMode', mode);
        return this;
    }
});

var Elp = El.prototype;


El.VISIBILITY = 1;

El.DISPLAY = 2;

El.OFFSETS = 3;

//给Element类增加方法
El.addMethods = function(o){
   Ext.apply(Elp, o);
};


Elp.on = Elp.addListener;
Elp.un = Elp.removeListener;


Elp.update = Elp.setHTML;

//返回 Ext.Element 对象.参数：节点的id, 一个DOM节点或一个存在的Element.
El.get = function(el){
    var extEl,
        dom,
        id;

    if(!el){
        return null;
    }

    if (typeof el == "string") { //文本形式的元素id
        if (!(dom = document.getElementById(el))) {//即document，没有查找到则直接返回
            return null;
        }
        if (Ext.cache[el] && Ext.cache[el].el) {//如果缓存中已经有了该DOM元素的Ext JS元素，更新其DOM引用
            extEl = Ext.cache[el].el;
            extEl.dom = dom;
        } else {
            extEl = El.addToCache(new El(dom));//如果没有，则新建一个Ext JS元素，其DOM属性指定该DOM元素
        }
        return extEl;
    } else if (el.tagName) { //DOM元素
        if(!(id = el.id)){//没有指定id，则分配id
            id = Ext.id(el);
        }
        if (Ext.cache[id] && Ext.cache[id].el) {//如果缓存中已经有了该DOM元素的Ext JS元素，更新其DOM引用
            extEl = Ext.cache[id].el;
            extEl.dom = el;
        } else {
            extEl = El.addToCache(new El(el)); //如果没有，则新建一个Ext JS元素，其DOM属性指定该DOM元素
        }
        return extEl;
    } else if (el instanceof El) {//Ext JS元素
        if(el != El.docEl){//非文档元素，document元素构建成的Ext JS元素
            
            
            el.dom = document.getElementById(el.id) || el.dom;
        }
        return el;
    } else if(el.isComposite) {//复合元素
        return el;
    } else if(Ext.isArray(el)) {//如果是数组形式的元素，构建成复合元素
        return El.select(el);
    } else if(el == document) {//document元素
        
        if(!El.docEl){//没有构建document的Ext JS元素，进行构建
            var F = function(){};
            F.prototype = Elp;
            El.docEl = new F();
            El.docEl.dom = document;
            El.docEl.id = Ext.id(document);
        }
        return El.docEl;
    }
    return null;
};

//在Ext.cache中注册以id值为名，以包括了el的对象为值的属性
El.addToCache = function(el, id){
    id = id || el.id;
    Ext.cache[id] = {
        el:  el,
        data: {},
        events: {}
    };
    return el;
};

//返回或设置el对应的Ext.cache的data的有关值
El.data = function(el, key, value) {
    el = El.get(el);
    if (!el) {//如果el不存在，则直接返回null
        return null;
    }
    var c = Ext.cache[el.id].data;
    if (arguments.length == 2) {//如果只设置了el和key,则返回以key为名的属性对应的值
        return c[key];
    }
    else {//如果设置了el、key和value，则将以key为名的属性赋值为value，并返回value
        return (c[key] = value);
    }
};




El.garbageCollect = function() {
    if (!Ext.enableGarbageCollector) {
        clearInterval(El.collectorThreadId);
    }
    else {
        var id,
            dom,
            EC = Ext.cache;

        for (id in EC) {
            if (!EC.hasOwnProperty(id)) {
                continue;
            }
            if(EC[id].skipGarbageCollection){
                continue;
            }
            dom = EC[id].el.dom;
            if(!dom || !dom.parentNode || (!dom.offsetParent && !document.getElementById(id))){
                if(Ext.enableListenerCollection){
                    Ext.EventManager.removeAll(dom);
                }
                delete EC[id];
            }
        }
    }
};


//设置this.dom
El.Flyweight = function(dom) {
    this.dom = dom;
};

var F = function(){};
F.prototype = Elp;

El.Flyweight.prototype = new F; //El.Flyweight.prototype有了Element.prototype的属性，对El.Flyweight.prototype的修改不影响Element
El.Flyweight.prototype.isFlyweight = true;

El._flyweights = {};

//Gets the globally shared flyweight Element, with the passed node as the active element
//el : String/HTMLElement The dom node or id
//named : String (optional) Allows for creation of named reusable flyweights to prevent conflicts (e.g. internally Ext uses "_global")
//Returns: Element The shared Element object (or null if no matching element was found)
El.fly = function(el, named) {
    var ret = null;
    named = named || '_global';

    el = Ext.getDom(el);
    if (el) {
        (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
        ret = El._flyweights[named];
    }

    return ret;
};


Ext.get = El.get;


Ext.fly = El.fly;



})();

Ext.applyIf(Ext.Element, {
    unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
    camelRe: /(-[a-z])/gi,
    opacityRe: /alpha\(opacity=(.*)\)/i,
    propertyCache: {},
    defaultUnit : "px",
    borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
    paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
    margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},

    addUnits : function(size, units) {
        if (size === "" || size == "auto" || size === null || size === undefined) {
            size = size || '';
        }
        else if (!isNaN(size) || !this.unitRe.test(size)) {
            size = size + (units || this.defaultUnit || 'px');
        }
        return size;
    },

    //Parses a number or string representing margin sizes into an object. 
    //Supports CSS-style margin declarations (e.g. 10, "10", "10 10", "10 10 10" 
    //and "10 10 10 10" are all valid options and would return the same result)
    //box : Number|String The encoded margins
    parseBox : function(box) {
        if (typeof box != 'string') {
            box = box.toString();
        }
        var parts  = box.split(' '),
            ln = parts.length;

        if (ln == 1) {
            parts[1] = parts[2] = parts[3] = parts[0];
        }
        else if (ln == 2) {
            parts[2] = parts[0];
            parts[3] = parts[1];
        }
        else if (ln == 3) {
            parts[3] = parts[1];
        }

        return {
            top   :parseFloat(parts[0]) || 0,
            right :parseFloat(parts[1]) || 0,
            bottom:parseFloat(parts[2]) || 0,
            left  :parseFloat(parts[3]) || 0
        };
    },
    
    //Parses a number or string representing margin sizes into an object. 
    //Supports CSS-style margin declarations (e.g. 10, "10", "10 10", "10 10 10" 
    //and "10 10 10 10" are all valid options and would return the same result)
    //box : Number|String The encoded margins
    //units : String The type of units to add
    unitizeBox : function(box, units) {
        var A = this.addUnits,
            B = this.parseBox(box);
            
        return A(B.top, units) + ' ' +
               A(B.right, units) + ' ' +
               A(B.bottom, units) + ' ' +
               A(B.left, units); //在B.left的基础上加上units或this.defaultUnit
        
    },

    //
    camelReplaceFn : function(m, a) {
        return a.charAt(1).toUpperCase();
    },

    //Normalizes(标准化) CSS property keys from dash(破折号) delimited(分隔) to camel case JavaScript Syntax(语法)
    //example: border-width -> borderWidth         padding-top -> paddingTop
    normalize : function(prop) {
        return this.propertyCache[prop] || (this.propertyCache[prop] = prop == 'float' ? 'cssFloat' : prop.replace(this.camelRe, this.camelReplaceFn));
    },

    //Retrieves the document height
    getDocumentHeight: function() {
        return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
    },

    //Retrieves the document width
    getDocumentWidth: function() {
        return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
    },

    //Retrieves the viewport height of the window.
    getViewportHeight: function(){
        return window.innerHeight;
    },

    //Retrieves the viewport width of the window.
    getViewportWidth : function() {
        return window.innerWidth;
    },

    //Returns the dimensions(尺寸) of the element available to lay content out in.
    //If the element (or any ancestor element) has CSS style display : none, the dimensions will be zero.
    getViewSize : function() {
        return {
            width: window.innerWidth,
            height: window.innerHeight
        };
    },

    //Retrieves the current orientation(方向) of the window. This is calculated by determing if the height is greater than the width.
    getOrientation : function() {
        if (Ext.supports.OrientationChange) {
            return (window.orientation == 0) ? 'portrait' : 'landscape';
        }
        
        return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
    },

    //Returns the top Element that is located at the passed coordinates(坐标) Function description
    fromPoint: function(x, y) {
        return Ext.get(document.elementFromPoint(x, y)); //根据坐标点获取元素
    }
});

Ext.applyIf(Ext.Element, {

    //<static> Returns the calculated(计算) CSS 2D transform offset(偏移) values (translate x and y)
    //el : Ext.Element/Element the element
    //Returns: Ext.util.Offset instance of Ext.util.Offset, with x and y properties
    getComputedTransformOffset: function(el) {
        if (el instanceof Ext.Element)
            el = el.dom;
            
        var transform = window.getComputedStyle(el).webkitTransform,
            cssMatrix = transform != 'none' ? new WebKitCSSMatrix(transform) : new WebKitCSSMatrix();

        if (typeof cssMatrix.m41 != 'undefined') {
            return new Ext.util.Offset(cssMatrix.m41, cssMatrix.m42);
        } else if (typeof cssMatrix.d != 'undefined') {
            return new Ext.util.Offset(cssMatrix.d, cssMatrix.e);
        }

        return new Ext.util.Offset(0, 0);
    },

    //<static> Transform an element using CSS 3
    //el : Ext.Element/Element the element
    //transforms : Object an object with all transformation to be applied. 
    //The keys are transformation method names, the values are arrays of params or a single number 
    //if there's only one param e.g: { translate: [0, 1, 2], scale: 0.5, skew: -25, rotate: 7 }
    cssTransform: function(el, transforms) {
        if (el instanceof Ext.Element)
            el = el.dom;

        var m = new WebKitCSSMatrix();

        Ext.iterate(transforms, function(n, v) {
            v = Ext.isArray(v) ? v : [v];
            m = m[n].apply(m, v);
        });

        
        
        if (Ext.supports.CSS3DTransform) {
            el.style.webkitTransform = 'matrix3d(' +
                                            m.m11+', '+m.m12+', '+m.m13+', '+m.m14+', '+
                                            m.m21+', '+m.m22+', '+m.m23+', '+m.m24+', '+
                                            m.m31+', '+m.m32+', '+m.m33+', '+m.m34+', '+
                                            m.m41+', '+m.m42+', '+m.m43+', '+m.m44+
                                       ')';
        } else {
            el.style.webkitTransform = m;
        }
    },

    //Translate an element using CSS 3 in 2D. 
    //This is supposed to be faster than cssTransform when we only need to translate 
    //an element without reserving(保留) its original(原) matrix(矩阵)
    //el : Ext.Element/Element the element
    //offset : Ext.util.Offset/Object The new offset with format { x: offsetX, y: offsetY }
	//Chrome12以上，translate3d效率高于translate
    cssTranslate: function(el, offset) {
        if (el instanceof Ext.Element)
            el = el.dom;

        if (Ext.supports.CSS3DTransform) {
            el.style.webkitTransform = 'translate3d('+offset.x+'px, '+offset.y+'px, 0px)';
        } else {
            el.style.webkitTransform = 'translate('+offset.x+'px, '+offset.y+'px)';
        }
    }

});


Ext.Element.addMethods({
    
    getY : function(el) {
        return this.getXY(el)[1];
    },

    //Gets the current X position of the element based on page coordinates. 
    //Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
    //Returns: Number The X position of the element
    getX : function(el) {
        return this.getXY(el)[0];
    },

    //Gets the current position of the element based on page coordinates. 
    //Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
    //Returns: Array The XY position of the element
    getXY : function() {
        
        var point = window.webkitConvertPointFromNodeToPage(this.dom, new WebKitPoint(0, 0));
        return [point.x, point.y];
    },

    //Returns the offsets of this element from the passed element. 
    //Both element must be part of the DOM tree and not have display:none to have page coordinates(坐标).
    //element : Mixed The element to get the offsets from.
    //Returns: Array The XY page offsets (e.g. [100, -200])
    getOffsetsTo : function(el){
        var o = this.getXY(),
            e = Ext.fly(el, '_internal').getXY();
        return [o[0]-e[0],o[1]-e[1]];
    },

    //Sets the position of the element in page coordinates, regardless of how the element is positioned(定位). 
    //pos : Array Contains X & Y [x, y] values for new position (coordinates are page-based)
    //Returns: Ext.Element this
    setXY : function(pos) {
        var me = this;

        if(arguments.length > 1) {
            pos = [pos, arguments[1]];
        }

        
        var pts = me.translatePoints(pos),
            style = me.dom.style;

        for (pos in pts) {
            if (!pts.hasOwnProperty(pos)) {
                continue;
            }
            if(!isNaN(pts[pos])) style[pos] = pts[pos] + "px";
        }
        return me;
    },

    
    setX : function(x){
        return this.setXY([x, this.getY()]);
    },

    
    setY : function(y) {
        return this.setXY([this.getX(), y]);
    },

    //Sets the element's left position directly using CSS style (instead of setX).
    setLeft : function(left) {
        this.setStyle('left', Ext.Element.addUnits(left));
        return this;
    },

    
    setTop : function(top) {
        this.setStyle('top', Ext.Element.addUnits(top));
        return this;
    },

    
    setTopLeft: function(top, left) {
        var addUnits = Ext.Element.addUnits;

        this.setStyle('top', addUnits(top));
        this.setStyle('left', addUnits(left));

        return this;
    },

    
    setRight : function(right) {
        this.setStyle('right', Ext.Element.addUnits(right));
        return this;
    },

    
    setBottom : function(bottom) {
        this.setStyle('bottom', Ext.Element.addUnits(bottom));
        return this;
    },

    
    getLeft : function(local) {
        return parseInt(this.getStyle('left'), 10) || 0;
    },

    
    getRight : function(local) {
        return parseInt(this.getStyle('right'), 10) || 0;
    },

    //Gets the top Y coordinate
    //local : Boolean True to get the local css position instead of page coordinate
    getTop : function(local) {
        return parseInt(this.getStyle('top'), 10) || 0;
    },

    
    getBottom : function(local) {
        return parseInt(this.getStyle('bottom'), 10) || 0;
    },

    //Sets the element's box. Use getBox() on another element to get a box obj. 
    //If animate(动画) is true then width, height, x and y will be animated concurrently(同时).
    //box : Object The box to fill {x, y, width, height}
    setBox : function(left, top, width, height) {
        var undefined;
        if (Ext.isObject(left)) {
            width = left.width;
            height = left.height;
            top = left.top;
            left = left.left;
        }
        
        if (left !== undefined) {
            this.setLeft(left);
        }
        if (top !== undefined) {
            this.setTop(top);
        }
        if (width !== undefined) {
            this.setWidth(width);
        }
        if (height !== undefined) {
            this.setHeight(height);
        }
    
        return this;
    },

    //Return an object defining the area of this Element which can be passed to 
    //setBox to set another Element's size/location to match this element.
    //contentBox : Boolean (optional) If true a box for the content of the element is returned.
    //local : Boolean (optional) If true the element's left and top are returned instead of page x/y.
    //Returns: Object box An object in the format
    //<pre><code> 
    //{ x: &lt;Element's X position>,
    //  y: &lt;Element's Y position>, 
    //  width: &lt;Element's width>, 
    //  height: &lt;Element's height>,
    //  bottom: &lt;Element's lower bound>, 
    //  right: &lt;Element's rightmost bound> } 
    //</code></pre> 
    //The returned object may also be addressed as an Array where index 0 
    //contains the X position and index 1 contains the Y position. 
    //So the result may also be used for setXY
    getBox : function(contentBox, local) {
        var me = this,
            dom = me.dom,
            width = dom.offsetWidth,
            height = dom.offsetHeight,
            xy, box, l, r, t, b;

        if (!local) {
            xy = me.getXY();
        }
        else if (contentBox) {
            xy = [0,0];
        }
        else {
            xy = [parseInt(me.getStyle("left"), 10) || 0, parseInt(me.getStyle("top"), 10) || 0];
        }

        if (!contentBox) {
            box = {
                x: xy[0],
                y: xy[1],
                0: xy[0],
                1: xy[1],
                width: width,
                height: height
            };
        }
        else {
            l = me.getBorderWidth.call(me, "l") + me.getPadding.call(me, "l");
            r = me.getBorderWidth.call(me, "r") + me.getPadding.call(me, "r");
            t = me.getBorderWidth.call(me, "t") + me.getPadding.call(me, "t");
            b = me.getBorderWidth.call(me, "b") + me.getPadding.call(me, "b");
            box = {
                x: xy[0] + l,
                y: xy[1] + t,
                0: xy[0] + l,
                1: xy[1] + t,
                width: width - (l + r),
                height: height - (t + b)
            };
        }

        box.left = box.x;
        box.top = box.y;
        box.right = box.x + box.width;
        box.bottom = box.y + box.height;

        return box;
    },

    //Return an object defining the area of this Element which can be passed to setBox 
    //to set another Element's size/location to match this element.
    //asRegion(optional) : Boolean If true an Ext.util.Region will be returned
    //Returns: Object box An object in the format
    //<pre><code> 
    //{ x: &lt;Element's X position>, 
    //  y: &lt;Element's Y position>, 
    //  width: &lt;Element's width>, 
    //  height: &lt;Element's height>, 
    //  bottom: &lt;Element's lower bound>, 
    //  right: &lt;Element's rightmost bound> } 
    //</code></pre> 
    //The returned object may also be addressed as an Array where index 0 contains the X position 
    //and index 1 contains the Y position. So the result may also be used for setXY
    getPageBox : function(getRegion) {
        var me = this,
            el = me.dom,
            w = el.offsetWidth,
            h = el.offsetHeight,
            xy = me.getXY(),
            t = xy[1],
            r = xy[0] + w,
            b = xy[1] + h,
            l = xy[0];
        
        if (!el) {
            return new Ext.util.Region();
        }
        
        if (getRegion) {
            return new Ext.util.Region(t, r, b, l);
        }
        else {
            return {
                left: l,
                top: t,
                width: w,
                height: h,
                right: r,
                bottom: b
            };
        }
    },

    //Translates the passed page coordinates into left/top css values for this element
    //x : Number/Array The page x or an array containing [x, y]
    //y : Number (optional) The page y, required if x is not an array
    //Returns: Object An object with left and top properties. e.g. {left: (value), top: (value)}
    translatePoints : function(x, y) {
        y = isNaN(x[1]) ? y : x[1];
        x = isNaN(x[0]) ? x : x[0];
        var me = this,
            relative = me.isStyle('position', 'relative'),
            o = me.getXY(),
            l = parseInt(me.getStyle('left'), 10),
            t = parseInt(me.getStyle('top'), 10);

        l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
        t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);

        return {left: (x - o[0] + l), top: (y - o[1] + t)};
    }
});

(function() {
    
    Ext.Element.classReCache = {};
    var El = Ext.Element,
        view = document.defaultView;

    El.addMethods({
        marginRightRe: /marginRight/i,
        trimRe: /^\s+|\s+$/g,
        spacesRe: /\s+/,

        //Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
        addCls: function(className) {
            var me = this,
                i,
                len,
                v,
                cls = [];

            if (!Ext.isArray(className)) {
                if (className && !this.hasCls(className)) {
                    me.dom.className += " " + className;
                }
            }
            else {
                for (i = 0, len = className.length; i < len; i++) {
                    v = className[i];
                    if (v && !me.hasCls(v)) {
                        cls.push(v);
                    }
                }
                if (cls.length) {
                    me.dom.className += " " + cls.join(" ");
                }
            }
            return me;
        },
        //
        addClass : function() {
            throw new Error("Component: addClass has been deprecated. Please use addCls.");
        },

        //Removes one or more CSS classes from the element.
        removeCls: function(className) {
            var me = this,
                i,
                idx,
                len,
                cls,
                elClasses;
            if (!Ext.isArray(className)) {
                className = [className];
            }
            if (me.dom && me.dom.className) {
                elClasses = me.dom.className.replace(this.trimRe, '').split(this.spacesRe);
                for (i = 0, len = className.length; i < len; i++) {
                    cls = className[i];
                    if (typeof cls == 'string') {
                        cls = cls.replace(this.trimRe, '');
                        idx = elClasses.indexOf(cls);
                        if (idx != -1) {
                            elClasses.splice(idx, 1);
                        }
                    }
                }
                me.dom.className = elClasses.join(" ");
            }
            return me;
        },
        
        removeClass : function() {
            throw new Error("Component: removeClass has been deprecated. Please use removeCls.");
        },

        //Puts a mask over this element to disable user interaction(互动). 
        //This method can only be applied to elements which accept child nodes.
        //msg : String (optional) A message to display in the mask. This can be html.
        //msgCls : String (optional) A css class to apply to the msg element
        //transparent : Boolean (optional) False to show make the mask gray(灰色) with opacity(不透明度). (defaults to true)
        mask: function(msg, msgCls, transparent) {
            var me = this,
                dom = me.dom,
                el = Ext.Element.data(dom, 'mask'),
                mask,
                size,
                cls = '';

            me.addCls('x-masked');
            if (me.getStyle("position") == "static") {
                me.addCls('x-masked-relative');
            }
            if (el) {
                el.remove();
            }
            if (Ext.isString(msgCls) && !Ext.isEmpty(msgCls)) {
                cls = ' ' + msgCls;
            }
            else {
                if (msgCls) {
                    cls = ' x-mask-gray';
                }
            }
                        
            mask = me.createChild({
                cls: 'x-mask' + ((transparent !== false) ? '' : ' x-mask-gray'),
                html: msg ? ('<div class="' + (msgCls || 'x-mask-message') + '">' + msg + '</div>') : ''
            });

            size = me.getSize();

            Ext.Element.data(dom, 'mask', mask);

            if (dom === document.body) {
                size.height = window.innerHeight;
                if (me.orientationHandler) {
                    Ext.EventManager.unOrientationChange(me.orientationHandler, me);
                }

                me.orientationHandler = function() {
                    size = me.getSize();
                    size.height = window.innerHeight;
                    mask.setSize(size);
                };

                Ext.EventManager.onOrientationChange(me.orientationHandler, me);
            }
            mask.setSize(size);
            if (Ext.is.iPad) {
                Ext.repaint();
            }
        },

        //Removes a previously applied mask.
        unmask: function() {
            var me = this,
                dom = me.dom,
                mask = Ext.Element.data(dom, 'mask');

            if (mask) {
                mask.remove();
                Ext.Element.data(dom, 'mask', undefined);
            }
            me.removeCls(['x-masked', 'x-masked-relative']);

            if (dom === document.body) {
                Ext.EventManager.unOrientationChange(me.orientationHandler, me);
                delete me.orientationHandler;
            }
        },

        //Adds one or more CSS classes to this element and removes the same class(es) from all siblings(兄弟姐妹).
        //className : String/Array The CSS class to add, or an array of classes
        radioCls: function(className) {
            var cn = this.dom.parentNode.childNodes,
                v;
            className = Ext.isArray(className) ? className: [className];
            for (var i = 0, len = cn.length; i < len; i++) {
                v = cn[i];
                if (v && v.nodeType == 1) {
                    Ext.fly(v, '_internal').removeCls(className);
                }
            };
            return this.addCls(className);
        },
        
        radioClass : function() {
            throw new Error("Component: radioClass has been deprecated. Please use radioCls.");
        },

        //Toggles(切换) the specified CSS class on this element (removes it if it already exists, otherwise adds it).
        toggleCls: function(className) {
            return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
        },
        
        toggleClass : function() {
            throw new Error("Component: toggleClass has been deprecated. Please use toggleCls.");
        },

        //Checks if the specified CSS class exists on this element's DOM node.
        hasCls: function(className) {
            return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
        },
        
        hasClass : function() {
            throw new Error("Element: hasClass has been deprecated. Please use hasCls.");
            return this.hasCls.apply(this, arguments);
        },

        
        replaceCls: function(oldClassName, newClassName) {
            return this.removeCls(oldClassName).addCls(newClassName);
        },
        
        replaceClass : function() {
            throw new Error("Component: replaceClass has been deprecated. Please use replaceCls.");
        },

        isStyle: function(style, val) {
            return this.getStyle(style) == val;
        },

        //Normalizes(标准化) currentStyle and computedStyle(计算过的样式).
        //prop : String The style property whose value is returned.
        //Returns: String The current value of the style property for this element.
        getStyle: function(prop) {
            var dom = this.dom,
                result,
                display,
                cs,
                platform = Ext.is,
                style = dom.style;

            prop = El.normalize(prop);
            cs = (view) ? view.getComputedStyle(dom, '') : dom.currentStyle;
            result = (cs) ? cs[prop] : null;

            
            if (result && !platform.correctRightMargin &&
                    this.marginRightRe.test(prop) &&
                    style.position != 'absolute' &&
                    result != '0px') {
                display = style.display;
                style.display = 'inline-block';
                result = view.getComputedStyle(dom, null)[prop];
                style.display = display;
            }

            result || (result = style[prop]);

            
            if (!platform.correctTransparentColor && result == 'rgba(0, 0, 0, 0)') {
                result = 'transparent';
            }

            return result;
        },

        //Wrapper(包装) for setting style properties, also takes single object parameter of multiple styles.
        //prop : String/Object The style property to be set, or an object of multiple styles.
        //value : String (optional) The value to apply to the given property, or null if an object was passed.
        setStyle: function(prop, value) {
            var tmp,
                style;

            if (typeof prop == 'string') {
                tmp = {};
                tmp[prop] = value;
                prop = tmp;
            }

            for (style in prop) {
                if (prop.hasOwnProperty(style)) {
                    this.dom.style[El.normalize(style)] = prop[style];
                }
            }

            return this;
        },

        //Applies a style specification to an element.
        //styles : String/Object/Function A style specification string e.g. 'width:100px', 
        //or object in the form {width:'100px'}, or a function which returns such a specification.
        applyStyles: function(styles) {
            if (styles) {
                var i,
                    len,
                    dom = this.dom;

                if (typeof styles == 'function') {
                    styles = styles.call();
                }
                if (typeof styles == 'string') {
                    styles = Ext.util.Format.trim(styles).split(/\s*(?::|;)\s*/);
                    for (i = 0, len = styles.length; i < len;) {
                        dom.style[El.normalize(styles[i++])] = styles[i++];
                    }
                }
                else if (typeof styles == 'object') {
                    this.setStyle(styles);
                }
            }
        },

        //Returns the offset height of the element
        //contentHeight : Boolean (optional) true to get the height minus borders and padding
        getHeight: function(contentHeight) {
            var dom = this.dom,
                height = contentHeight ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight;
            return height > 0 ? height: 0;
        },

        
        getWidth: function(contentWidth) {
            var dom = this.dom,
                width = contentWidth ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth;
            return width > 0 ? width: 0;
        },

        
        setWidth: function(width) {
            var me = this;
                me.dom.style.width = El.addUnits(width);
            return me;
        },

        
        setHeight: function(height) {
            var me = this;
                me.dom.style.height = El.addUnits(height);
            return me;
        },

        //Set the size of this Element. If animation is true, both width and height will be animated concurrently.
        setSize: function(width, height) {
            var me = this,
                style = me.dom.style;

            if (Ext.isObject(width)) {
                
                height = width.height;
                width = width.width;
            }

            style.width = El.addUnits(width);
            style.height = El.addUnits(height);
            return me;
        },

        //Gets the width of the border(s) for the specified side(s)
        //side : String Can be t, l, r, b or any combination of those to add multiple values. 
        //For example, passing 'lr' would get the border left width + the border right width.
        getBorderWidth: function(side) {
            return this.sumStyles(side, El.borders);
        },

        
        getPadding: function(side) {
            return this.sumStyles(side, El.paddings);
        },

        
        getMargin: function(side) {
            return this.sumStyles(side, El.margins);
        },

        //Returns the dimensions(尺寸) of the element available to lay content out in.
        //If the element (or any ancestor element) has CSS style display : none, the dimensions will be zero.
        getViewSize: function() {
            var doc = document,
                dom = this.dom;

            if (dom == doc || dom == doc.body) {
                return {
                    width: El.getViewportWidth(),
                    height: El.getViewportHeight()
                };
            }
            else {
                return {
                    width: dom.clientWidth,
                    height: dom.clientHeight
                };
            }
        },

        //Returns the size of the element.
        //contentSize : Boolean (optional) true to get the width/size minus borders and padding
        getSize: function(contentSize) {
            var dom = this.dom;
            return {
                width: Math.max(0, contentSize ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth),
                height: Math.max(0, contentSize ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight)
            };
        },

        //Forces the browser to repaint this element
        repaint: function() {
            var dom = this.dom;
                this.addCls("x-repaint");
            dom.style.background = 'transparent none';
            setTimeout(function() {
                dom.style.background = null;
                Ext.get(dom).removeCls("x-repaint");
            },
            1);
            return this;
        },

        
        getOuterWidth: function() {
            return this.getWidth() + this.getMargin('lr');
        },

        //Retrieves the width of the element accounting(会计) for the left and right margins.
        getOuterHeight: function() {
            return this.getHeight() + this.getMargin('tb');
        },

        
        sumStyles: function(sides, styles) {
            var val = 0,
                m = sides.match(/\w/g),
                len = m.length,
                s,
                i;

            for (i = 0; i < len; i++) {
                s = m[i] && parseFloat(this.getStyle(styles[m[i]])) || 0;
                if (s) {
                    val += Math.abs(s);
                }
            }
            return val;
        }
    });
})();

Ext.Element.addMethods({
    //Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
    //selector : String The simple selector to test
    //maxDepth : Number/Mixed (optional) The max depth to search as a number or element (defaults to 50 || document.body)
    //returnEl : Boolean (optional) True to return a Ext.Element object instead of DOM node
    findParent : function(simpleSelector, maxDepth, returnEl) {
        var p = this.dom,
            b = document.body,
            depth = 0,
            stopEl;

        maxDepth = maxDepth || 50;
        if (isNaN(maxDepth)) {
            stopEl = Ext.getDom(maxDepth);
            maxDepth = Number.MAX_VALUE;
        }
        while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
            if (Ext.DomQuery.is(p, simpleSelector)) {
                return returnEl ? Ext.get(p) : p;
            }
            depth++;
            p = p.parentNode;
        }
        return null;
    },
    
    //Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
    //Returns: HTMLElement The matching DOM node (or null if no match was found)
    findParentNode : function(simpleSelector, maxDepth, returnEl) {
        var p = Ext.fly(this.dom.parentNode, '_internal');
        return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
    },

    //Walks up the dom looking for a parent node that matches the passed simple selector
    // (e.g. div.some-class or span:first-child). This is a shortcut for findParentNode() that always returns an Ext.Element.
    up : function(simpleSelector, maxDepth) {
        return this.findParentNode(simpleSelector, maxDepth, true);
    },

    //Selects elements based on the passed CSS selector to enable Element methods 
    //to be applied to many related elements in one statement through the returned CompositeElement or CompositeElement object.
    //selector : String/Array The CSS selector or an array of elements
    //root : HTMLElement/String (optional) The root element of the query or id of the root
    //Returns: CompositeElement
    select : function(selector, composite) {
        return Ext.Element.select(selector, this.dom, composite);
    },

    //Selects child nodes based on the passed CSS selector (the selector should not contain an id).
    query : function(selector) {
        return Ext.DomQuery.select(selector, this.dom);
    },

    //Selects a single child at any depth below this element based on the passed CSS selector 
    //(the selector should not contain an id).
    //selector : String The CSS selector
    //returnDom : Boolean (optional) True to return the DOM node instead of Ext.Element (defaults to false)
    down : function(selector, returnDom) {
        var n = Ext.DomQuery.selectNode(selector, this.dom);
        return returnDom ? n : Ext.get(n);
    },

    //Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
    //selector : String The CSS selector
    //returnDom : Boolean (optional) True to return the DOM node instead of Ext.Element (defaults to false)
    child : function(selector, returnDom) {
        var node,
            me = this,
            id;
        id = Ext.get(me).id;
        
        id = id.replace(/[\.:]/g, "\\$0");
        node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
        return returnDom ? node : Ext.get(node);
    },

    //Gets the parent node for this element, optionally chaining up trying to match a selector 
    parent : function(selector, returnDom) {
        return this.matchNode('parentNode', 'parentNode', selector, returnDom);
    },

     
    next : function(selector, returnDom) {
        return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
    },

    
    prev : function(selector, returnDom) {
        return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
    },


    //Gets the first child, skipping text nodes
    first : function(selector, returnDom) {
        return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
    },

    
    last : function(selector, returnDom) {
        return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
    },

    matchNode : function(dir, start, selector, returnDom) {
        if (!this.dom)
            return null;
        
        var n = this.dom[start];
        while (n) {
            if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
                return !returnDom ? Ext.get(n) : n;
            }
            n = n[dir];
        }
        return null;
    }
});


Ext.Element.addMethods({
    //Gets the Scroller instance of the first parent that has one.
    getScrollParent : function() {
        var parent = this.dom, scroller;
        while (parent && parent != document.body) {
            if (parent.id && (scroller = Ext.ScrollManager.get(parent.id))) {
                return scroller;
            }
            parent = parent.parentNode;
        }
        return null;
    }
});


Ext.Element.addMethods({
    
    appendChild : function(el) {
        return Ext.get(el).appendTo(this);
    },

    
    appendTo : function(el) {
        Ext.getDom(el).appendChild(this.dom);
        return this;
    },

    
    insertBefore : function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el);
        return this;
    },

    
    insertAfter : function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el.nextSibling);
        return this;
    },

    
    insertFirst : function(el, returnDom) {
        el = el || {};
        if (el.nodeType || el.dom || typeof el == 'string') { 
            el = Ext.getDom(el);
            this.dom.insertBefore(el, this.dom.firstChild);
            return !returnDom ? Ext.get(el) : el;
        }
        else { 
            return this.createChild(el, this.dom.firstChild, returnDom);
        }
    },

    
    insertSibling: function(el, where, returnDom){
        var me = this, rt,
        isAfter = (where || 'before').toLowerCase() == 'after',
        insertEl;

        if(Ext.isArray(el)){
            insertEl = me;
            Ext.each(el, function(e) {
                rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
                if(isAfter){
                    insertEl = rt;
                }
            });
            return rt;
        }

        el = el || {};

        if(el.nodeType || el.dom){
            rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
            if (!returnDom) {
                rt = Ext.get(rt);
            }
        }else{
            if (isAfter && !me.dom.nextSibling) {
                rt = Ext.DomHelper.append(me.dom.parentNode, el, !returnDom);
            } else {
                rt = Ext.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
            }
        }
        return rt;
    },

    
    replace : function(el) {
        el = Ext.get(el);
        this.insertBefore(el);
        el.remove();
        return this;
    },
    
    
    replaceWith: function(el){
        var me = this;
            
        if(el.nodeType || el.dom || typeof el == 'string'){
            el = Ext.get(el);
            me.dom.parentNode.insertBefore(el, me.dom);
        }else{
            el = Ext.DomHelper.insertBefore(me.dom, el);
        }
        
        delete Ext.cache[me.id];
        Ext.removeNode(me.dom);      
        me.id = Ext.id(me.dom = el);
        Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);     
        return me;
    },
    
    //Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child 
    //config : Object DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) 
    //then a div will be automatically generated(产生) with the specified attributes.
    //insertBefore : HTMLElement (optional) a child element of this element
    //returnDom : Boolean (optional) true to return the dom node instead of creating an Element
    createChild : function(config, insertBefore, returnDom) {
        config = config || {tag:'div'};
        if (insertBefore) {
            return Ext.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
        }
        else {
            return Ext.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
        }
    },

    //Creates and wraps(包装) this element with another element
    wrap : function(config, returnDom) {
        var newEl = Ext.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
        newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
        return newEl;
    },

    //Inserts an html fragment into this element
    //where : String Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
    //html : String The HTML fragment
    //returnEl : Boolean (optional) True to return an Ext.Element (defaults to false)
    //Returns: HTMLElement/Ext.Element The inserted node (or nearest related if more than 1 inserted)
    insertHtml : function(where, html, returnEl) {
        var el = Ext.DomHelper.insertHtml(where, this.dom, html);
        return returnEl ? Ext.get(el) : el;
    }
});


Ext.Element.addMethods({
    //Gets the x,y coordinates specified by the anchor position on the element.
    //anchor : String (optional) The specified anchor position (defaults to "c"). 
    //See alignTo for details on supported anchor positions.
    //size : Object (optional) An object containing the size to use for calculating anchor position 
    //{width: (target width), height: (target height)} (defaults to the element's current size)
    //Returns: Array [x, y] An array containing the element's x and y coordinates
    getAnchorXY: function(anchor, local, size) {
        
        
        anchor = (anchor || "tl").toLowerCase();
        size = size || {};

        var me = this,
            vp = me.dom == document.body || me.dom == document,
            width = size.width || vp ? window.innerWidth: me.getWidth(),
            height = size.height || vp ? window.innerHeight: me.getHeight(),
            xy,
            rnd = Math.round,
            myXY = me.getXY(),
            extraX = vp ? 0: !local ? myXY[0] : 0,
            extraY = vp ? 0: !local ? myXY[1] : 0,
            hash = {
                c: [rnd(width * 0.5), rnd(height * 0.5)],
                t: [rnd(width * 0.5), 0],
                l: [0, rnd(height * 0.5)],
                r: [width, rnd(height * 0.5)],
                b: [rnd(width * 0.5), height],
                tl: [0, 0],
                bl: [0, height],
                br: [width, height],
                tr: [width, 0]
            };

        xy = hash[anchor];
        return [xy[0] + extraX, xy[1] + extraY];
    },

    //Gets the x,y coordinates to align this element with another element. 
    //See alignTo for more info on the supported position values.
    //element : Mixed The element to align to.
    //position : String (optional, defaults to "tl-bl?") The position to align to.
    //offsets : Array (optional) Offset the positioning by [x, y]
    //Returns: Array [x, y]
    getAlignToXY: function(el, position, offsets) {
        el = Ext.get(el);

        if (!el || !el.dom) {
            throw new Error("Element.alignToXY with an element that doesn't exist");
        }
        offsets = offsets || [0, 0];

        if (!position || position == '?') {
            position = 'tl-bl?';
        }
        else if (! (/-/).test(position) && position !== "") {
            position = 'tl-' + position;
        }
        position = position.toLowerCase();

        var me = this,
            matches = position.match(/^([a-z]+)-([a-z]+)(\?)?$/),
            dw = window.innerWidth,
            dh = window.innerHeight,
            p1 = "",
            p2 = "",
            a1,
            a2,
            x,
            y,
            swapX,
            swapY,
            p1x,
            p1y,
            p2x,
            p2y,
            width,
            height,
            region,
            constrain;

        if (!matches) {
            throw "Element.alignTo with an invalid alignment " + position;
        }

        p1 = matches[1];
        p2 = matches[2];
        constrain = !!matches[3];

        
        
        a1 = me.getAnchorXY(p1, true);
        a2 = el.getAnchorXY(p2, false);

        x = a2[0] - a1[0] + offsets[0];
        y = a2[1] - a1[1] + offsets[1];

        if (constrain) {
            width = me.getWidth();
            height = me.getHeight();

            region = el.getPageBox();

            
            
            
            p1y = p1.charAt(0);
            p1x = p1.charAt(p1.length - 1);
            p2y = p2.charAt(0);
            p2x = p2.charAt(p2.length - 1);

            swapY = ((p1y == "t" && p2y == "b") || (p1y == "b" && p2y == "t"));
            swapX = ((p1x == "r" && p2x == "l") || (p1x == "l" && p2x == "r"));

            if (x + width > dw) {
                x = swapX ? region.left - width: dw - width;
            }
            if (x < 0) {
                x = swapX ? region.right: 0;
            }
            if (y + height > dh) {
                y = swapY ? region.top - height: dh - height;
            }
            if (y < 0) {
                y = swapY ? region.bottom: 0;
            }
        }

        return [x, y];
    }
});
